package ycl.gateway.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.xiaoymin.knife4j.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import ycl.common.ApiResult;
import ycl.common.constants.RedisConstant;
import ycl.common.enums.ApiResultEnum;
import ycl.gateway.constants.CurrencyConstant;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author ycl
 * @date 2022-03-17 14:13:04
 */
@Component
@Slf4j
public class AccessConfig implements GlobalFilter, Ordered {

	@Resource
	private StringRedisTemplate stringRedisTemplate;

	private final AntPathMatcher antPathMatcher = new AntPathMatcher();

	@Override
	public int getOrder() {
		return 0;
	}

	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		log.info("=======请求拦截=====" + JSON.toJSONString(antPathMatcher));
		//直接放行接口
		Map<String, String> map = new HashMap<>();
		map.put("/**/v2/api-docs", "swagger资源");
		map.put("/ycl-security/login/*", "登录接口");
		map.put("/ycl-message/email/*","邮件接口");

		String path = exchange.getRequest().getPath().value();
		log.info("path --------> {}", path);
		for (Map.Entry<String, String> entry : map.entrySet()) {
			if (antPathMatcher.match(entry.getKey(), path)) {
				log.info("------------ {} ------------ {} ------------", entry.getValue(), exchange);
				return chain.filter(exchange);
			}
		}

		if (antPathMatcher.match("/ycl-security/**", path))
			return chain.filter(exchange);

		//如果不是请求到权限模块的就校验token
		ServerHttpResponse originalResponse = exchange.getResponse();
		DataBufferFactory bufferFactory = originalResponse.bufferFactory();
		ServerHttpResponseDecorator response = getResponse(originalResponse, bufferFactory);
		response.getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8);

		String token = extractToken(exchange.getRequest());
		if (StrUtil.isBlank(token)) {
			originalResponse.setStatusCode(HttpStatus.OK);
			return response.writeWith(Flux.just(bufferFactory.allocateBuffer()));
		}

		//默认只要获取到数据就是已登录成功的用户
		String v = stringRedisTemplate.opsForValue().get(RedisConstant.getLoginUserKey(token));
		if (StrUtil.isBlank(v)) {
			originalResponse.setStatusCode(HttpStatus.OK);
			return response.writeWith(Flux.just(bufferFactory.allocateBuffer()));
		}
		return chain.filter(exchange);
	}

	/**
	 * 获取token
	 *
	 * @param request 请求头
	 * @return
	 */
	private static String extractToken(ServerHttpRequest request) {
		List<String> strings = request.getHeaders().get(CurrencyConstant.TOKEN);
		//获取token
		String authToken = null;
		if (strings != null) {
			authToken = strings.get(0);
		}
		log.info("gateway token ==========> {}", authToken);
		return authToken;
	}

	@SuppressWarnings("all")
	private ServerHttpResponseDecorator getResponse(ServerHttpResponse response, DataBufferFactory factory) {
		return new ServerHttpResponseDecorator(response) {
			@Override
			public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {

				if (!(body instanceof Flux))
					return super.writeWith(body);

				Flux<? extends DataBuffer> flux = (Flux<? extends DataBuffer>) body;
				return super.writeWith(flux.buffer().map(dataBuffers -> {
					DataBufferFactory dbf = new DefaultDataBufferFactory();
					DataBuffer join = dbf.join(dataBuffers);
					byte[] content = new byte[join.readableByteCount()];
					join.read(content);

					//释放内存
					DataBufferUtils.release(join);
					JSONObject json = new JSONObject();
					ApiResult<Object> result = ApiResult.fail(ApiResultEnum.SC_UNAUTHORIZED);
					String s = JSONObject.toJSONString(result);
					System.out.println("gateway 响应非法报文 ---> " + s);

					byte[] bytes = s.getBytes(StandardCharsets.UTF_8);
					//不重新设置长度就无法显示
					this.getDelegate().getHeaders().setContentLength(bytes.length);
					return factory.wrap(bytes);
				}));
			}
		};
	}
}
